

#include <string.h>
#include "rtthread.h"
#include "ev_trans.h"
#include "ev_cmd_type.h"
#include "ev_flash.h"
#include "board.h"
#include "ev_version.h"

static int _cmp_packet(const ev_packet_t *p1, const ev_packet_t *p2)
{
    if (p1 == p2) {
        return 0;
    }
    if (!p1 || !p2) {
        return -1;
    }
    if (p1->user != p2->user) {
        return -2;
    }
    if (p1->seq != p2->seq) {
        return -3;
    }
    if (p1->cmd != p2->cmd) {
        return -4;
    }
    if (p1->len != p2->len) {
        return -5;
    }
    if (p1->len > EV_PACKET_MAX_DATA_LEN || p2->len > EV_PACKET_MAX_DATA_LEN) {
        return -6;
    }
    if (memcmp(p1->data, p2->data, p1->len)) {
        return -7;
    }
    return 0;
}

static void _process_get_version(const ev_packet_t *in, ev_packet_t* out)
{
    // ev_call_get_version_req_t *req = (ev_call_get_version_req_t *)in->data;
    ev_call_get_version_rsp_t *rsp = (ev_call_get_version_rsp_t *)out->data;
    if (in->len != sizeof(ev_call_get_version_req_t)) {
        rsp->ret = 0xff;
    } else {
        rsp->major = EV_VERSION_MAJOR;
        rsp->minor = EV_VERSION_MINOR;
        rsp->patch = EV_VERSION_PATCH;
        rsp->build_time = 0;
        rsp->firm_len = 1024*100;
        rsp->ret = 0;
    }
    out->len = sizeof(ev_call_get_version_rsp_t);
}

static void _process_ota_read(const ev_packet_t *in, ev_packet_t* out)
{
    ev_call_ota_read_req_t *req = (ev_call_ota_read_req_t *)in->data;
    ev_call_ota_read_rsp_t *rsp = (ev_call_ota_read_rsp_t *)out->data;
    if (in->len != sizeof(ev_call_ota_read_req_t)) {
        rsp->ret = 0xff;
    } else {
        int ret = ev_ota_read(FIRMWARE_TYPE_APP1, req->idx * sizeof(rsp->data), rsp->data, sizeof(rsp->data));
        rsp->ret = (uint8_t)(-ret);
    }
    out->len = sizeof(ev_call_ota_read_rsp_t);
}

static void _process_ota_start(const ev_packet_t *in, ev_packet_t* out)
{
    ev_call_ota_start_req_t *req = (ev_call_ota_start_req_t *)in->data;
    ev_call_ota_start_rsp_t *rsp = (ev_call_ota_start_rsp_t *)out->data;
    if (in->len != sizeof(ev_call_ota_start_req_t)) {
        rsp->ret = 0xff;
    } else {
        if (req->firm_len > APP2_SIZE) {
            rsp->ret = 1;
        } else {
            if (ev_ota_erase(FIRMWARE_TYPE_APP2)) {
                rsp->ret = 2;
            } else {
                rsp->ret = 0;
            }
        }
    }
    out->len = sizeof(ev_call_ota_start_rsp_t);
}

static void _process_ota_data(const ev_packet_t *in, ev_packet_t* out)
{
    ev_call_ota_data_req_t *req = (ev_call_ota_data_req_t *)in->data;
    ev_call_ota_data_rsp_t *rsp = (ev_call_ota_data_rsp_t *)out->data;
    if (in->len != sizeof(ev_call_ota_data_req_t)) {
        rsp->ret = 0xff;
    } else {
        int ret = ev_ota_write(FIRMWARE_TYPE_APP2, req->idx * sizeof(req->data), req->data, sizeof(req->data));
        rsp->ret = (uint8_t)(-ret);
    }
    out->len = sizeof(ev_call_ota_data_rsp_t);
}
static void _process_ota_end(const ev_packet_t *in, ev_packet_t* out)
{
    ev_call_ota_end_rsp_t *rsp = (ev_call_ota_end_rsp_t *)out->data;
    if (in->len != 0) {
        rsp->ret = 0xff;
    } else {
        int ret = ev_ota_check(FIRMWARE_TYPE_APP2);
        rsp->ret = (uint8_t)(-ret);
    }
    out->len = sizeof(ev_call_ota_end_rsp_t);
}

static void _process_reset(const ev_packet_t *in, ev_packet_t* out)
{
    ev_call_reset_req_t* req = (ev_call_reset_req_t*)in->data;
    ev_call_reset_rsp_t* rsp = (ev_call_reset_rsp_t*)out->data;

    if (in->len != sizeof(ev_call_reset_req_t)) {
        rsp->ret = 0xff;
    } else {
        driv_cpu_reboot(req->delay_ms);
        rsp->ret = 0;
    }
    out->len = sizeof(ev_call_reset_rsp_t);
}

typedef struct {
    uint16_t cmd;
    void (*func)(const ev_packet_t *in, ev_packet_t* out);
} cmd_handler_t;

static const cmd_handler_t cmd_table[] = {
    {EV_CALL_GET_VERSION,   &_process_get_version},
    {EV_CALL_OTA_READ,      &_process_ota_read},
    {EV_CALL_OTA_START,     &_process_ota_start},
    {EV_CALL_OTA_DATA,      &_process_ota_data},
    {EV_CALL_OTA_END,       &_process_ota_end},
    {EV_CALL_RESET,         &_process_reset},
};

void ev_comm_process(const ev_packet_t *in)
{
    static ev_packet_t last_native_in, last_router_in;
    static ev_packet_t last_native_out, last_router_out;

    if (in->cmd & 0x01) { // invalid cmd
        rt_kprintf("ev_comm_process: invalid cmd: %d\n", in->cmd);
        return;
    }

    ev_packet_t *last_in_ptr, *last_out_ptr;
    if (in->user) {
        last_in_ptr = &last_router_in;
        last_out_ptr = &last_router_out;
    } else {
        last_in_ptr = &last_native_in;
        last_out_ptr = &last_native_out;
    }

    if (!_cmp_packet(in, last_in_ptr)) {
        rt_kprintf("ev_comm_process: receive a same packet\n");
        ev_trans_tx(last_out_ptr);
        return ;
    }

    for (uint32_t i = 0; i < sizeof(cmd_table) / sizeof(cmd_table[0]); i ++) {
        if (cmd_table[i].cmd == in->cmd) {
            cmd_table[i].func(in, last_out_ptr);
            last_out_ptr->user = in->user;
            last_out_ptr->seq = in->seq;
            last_out_ptr->cmd = in->cmd + 1;

            ev_trans_tx(last_out_ptr);
            memcpy(last_in_ptr, in, sizeof(ev_packet_t));
            return;
        }
    }
    rt_kprintf("ev_comm_process: unknow cmd: %d\n", in->cmd);
}
